//------------------------------------------------------------------------------
#include <avr/io.h>
#include <avr/interrupt.h>
#include "uart.h"

enum
{
	FIFO_OK,
	FIFO_ERR = -1	// Same as stdio EOF
};

#define DisableUartISR() 	UCSR0B &= ~_BV(UDRIE0)	// Disable USART_UDRE interrupts.
#define EnableUartISR()   	UCSR0B |=  _BV(UDRIE0)	// Enable USART_UDRE interrupts.

unsigned char UARTTxFifo[TX_BUFSZ + sizeof(UARTFifo) - 1];
const pUARTFifo TxBuf = (pUARTFifo)UARTTxFifo;

unsigned char UARTRxFifo[RX_BUFSZ + sizeof(UARTFifo) - 1];
const pUARTFifo RxBuf = (pUARTFifo)UARTRxFifo;

void InitUARTBuffer( void )
{
	TxBuf->in = 0;
	TxBuf->out = 0;
	TxBuf->size = TX_BUFSZ;

	RxBuf->in = 0;
	RxBuf->out = 0;
	RxBuf->size = RX_BUFSZ;		
}

int UartPutFifo(pUARTFifo p, char c)
{
	unsigned char t = p->in+1;	
	int rst = FIFO_OK;
	
	if (t >= p->size)
	{
		t = 0;
	}
	
	if (t == p->out)	// isFull()
	{
		rst = FIFO_ERR;
	}
	else
	{
		p->buf[p->in] = c;
		p->in = t;	
	}
	
	return rst;
}

unsigned int UartPullFifo(pUARTFifo p)
{
	if (p->in == p->out)	// isEmpty()
	{
		return FIFO_ERR;
	}
	
	unsigned int  c = p->buf[p->out];
	unsigned char t = p->out+1;
	if (t >= p->size)
		t = 0;
	p->out = t;
	
	return c;
}


int isBufferNotEmpty( pUARTFifo p )
{
	if (p->in == p->out)
		return FALSE;
	else
		return TRUE;
}


void InitUART(uint16_t ubrr)
{

	UBRR0L = ubrr;
	UBRR0H = (uint8_t)(ubrr>>8);
	UCSR0A = _BV(U2X0);
	
	// UBRR0L = 51;
	// UBRR0H = 0;

	
	UCSR0B = _BV(RXEN0) | _BV(TXEN0) | _BV(RXCIE0); //    ((1<<TXEN) | (1<<RXEN) | (1<<RXCIE));
	UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); //((1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0));

		
	InitUARTBuffer();
	
	stdout = stdin = &serial;
}

int isTxNotEmpty( void )
{
	return isBufferNotEmpty(TxBuf);
}

int isRxNotEmpty( void )
{
	return isBufferNotEmpty(RxBuf);
}


int put_c(char c, FILE *stream)	// Non blocking output
{
	int retc;
	retc = UartPutFifo(TxBuf, c);
	EnableUartISR();
	return retc;
}

int put_char(char c, FILE *stream)	// Blocking output
{
	UartPutFifo(TxBuf, c);
	EnableUartISR();	
	return 0;
}

int get_c(FILE *stream)	// Non blocking, return status outside of char range
{
	int retc = UartPullFifo(RxBuf);
	return retc;
}

int get_char(FILE *stream)	// Blocks waiting for something
{
	return UartPullFifo(RxBuf);
}

FILE serial = FDEV_SETUP_STREAM(put_c,
								get_c,
								_FDEV_SETUP_RW);

// We don't care if the buffer is full.  Just signal we got one.
// The task may attempt one extra time to get data out of an empyt
// buffer (getc(), but so what.  Eventually it will block waiting
// for another character to be received.


// UART Receive Complete Interrupt Handler
ISR( USART_RX_vect )
{
    DisableUartISR();			// Disable Rx interrupt
    //sei();					// Allow other interrupt activity to occur
	UartPutFifo(RxBuf, UDR0);	// This resets the Rx Interrupt
	
	EnableUartISR();			// Re-enable.
}


// USART Data Register Empty  Interrupt Handler
ISR( USART_UDRE_vect )
{
    DisableUartISR();				// Disable UDRE interrupt
    //sei();						// Allow other stuff to happen
	int c = UartPullFifo(TxBuf);	// Return -1 if empty (enables interrupts)
	if (c >= 0)						// Tricky tight code: only checking sign
	{
		UDR0 = c;
		EnableUartISR();
	}
}


// UART Transmit Complete Interrupt Handler
ISR( USART_TX_vect )
{
    DisableUartISR();	

	EnableUartISR();
}
